package com.chencq.core.utils.beanutil;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 范型工具类,通过反射获得Class声明的范型类
 * 
 * @version 2009-03-23 jxc 防止没有设置T导致异常<br>
 *          2008-06-12 jxc 增加递归查找范型的处理 2006-11-01 jxc 引自springside
 * @author jxc
 */
public class GenericsUtils {
	/**
	 * 禁止创建实例
	 */
	private GenericsUtils() {
	}

	/**
	 * 获得第一个声明的范型类
	 * 
	 * @param clazz The class to introspect
	 * @return the first generic declaration, or <code>Object.class</code> if
	 *         cannot be determined
	 */
	public static <E> Class<E> getGenericClass(Class<?> clazz) {
		return getGenericClass(clazz, 0);
	}

	/**
	 * 获得第 n 个声明的范型类
	 * 
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration,start from 0.
	 */
	public static <E> Class<E> getGenericClass(Class<?> clazz, int index) throws IndexOutOfBoundsException {

		Class<E>[] classes = getGenericClasses(clazz);

		if (classes == null)
			return null;

		if (index >= classes.length || index < 0) {
			throw new IndexOutOfBoundsException("Index: " + index + ", Size of Parameterized Type: " + classes.length);
		}
		return classes[index];

	}

	/**
	 * 获得所有的范型类.
	 * 
	 * @param clazz
	 * @return 获得注册在某个类上的泛型实体类
	 */
	public static <E> Class<E>[] getGenericClasses(Class<?> clazz) {
		Type genType = null;

		// 增加递归处理功能
		// 防止多次继承后出现问题
		while (!(clazz.getName().equals(Object.class.getName()))) {
			genType = clazz.getGenericSuperclass();
			if (genType instanceof ParameterizedType) {
				break;
			}
			clazz = clazz.getSuperclass();
		}
		if (clazz.getName().equals(Object.class.getName())) {
			return null;// Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		Class<E>[] classes = new Class[params.length];

		for (int i = 0; i < params.length; i++) {
			// 防止T没有定义实例造成转型异常
			if (Class.class.isAssignableFrom(params[i].getClass()))
				classes[i] = (Class<E>) params[i];
		}
		return classes;
	}
}
